package com.jgzx.filter;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.jgzx.common.R;
import com.jgzx.common.ResultCode;
import com.jgzx.config.UrlConfigProperties;
import com.jgzx.constant.CacheConstants;
import com.jgzx.constant.Constants;
import com.jgzx.domain.LoginUser;
import com.jgzx.redis.config.RedisEnum;
import com.jgzx.redis.login.LoginCache;
import com.jgzx.redis.service.RedisService;
import com.jgzx.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Map;


/**
 * @author jackesy
 * @className: GatewayGlobalFilter
 * @description: 网关全局过滤器
 * @create 2021/2/6 17:20
 **/
@Slf4j
@Component
public class GatewayGlobalFilter implements GlobalFilter, Ordered {

    @Autowired
    private UrlConfigProperties urlConfigProperties;

    @Autowired
    private RedisService redisService;

    /**
     * 根据请求token获取出用户标识，放在header传递到业务系统
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String url = request.getURI().getPath();
        String method = request.getMethodValue();
        log.info("请求URL:{}, 请求method:{}", url, method);
        Map<String, String> header = request.getHeaders().toSingleValueMap();
        log.info("请求头信息：{}", JSON.toJSONString(header));
        //1、获取token 请求路径参数
        String token = request.getQueryParams().getFirst(Constants.HEADER_TOKEN);
        if (StrUtil.isEmpty(token)) {
            // 请求头获取token
            token = header.get(Constants.HEADER_TOKEN);
        }
        Long user_id = null;
        String phone_num = null;
        Long enterprise_id = null;
        //2、不需要登录的路径不验证token
        if (!StringUtil.matches(url, urlConfigProperties.getWhites())) {
            if (StrUtil.isEmpty(token)) {
                return this.setUnauthorizedResponse(exchange, ResultCode.UN_LOGIN, "登录令牌不能为空");
            }
        }
        if(StrUtil.isNotEmpty(token)){
            String token_key = LoginCache.getTokenKey(token);
            if (StrUtil.isEmpty(token_key)) {
                return this.setUnauthorizedResponse(exchange, ResultCode.UN_AUTHORIZED, "令牌验证失败");
            }
            //3、根据token从缓存获取登录的用户信息
            LoginUser loginUser = LoginCache.getLoginUser(token_key);
            if (ObjectUtil.isNull(loginUser)) {
                return this.setUnauthorizedResponse(exchange, ResultCode.UN_LOGIN, "登录状态已过期");
            }
            if (!StrUtil.equalsIgnoreCase(token, loginUser.getToken())) {
                return this.setUnauthorizedResponse(exchange, ResultCode.UN_LOGIN, "你已在其他地方登录");
            }
            user_id = loginUser.getUser_id();
            phone_num = loginUser.getPhone_num();
            enterprise_id = loginUser.getEnterprise_id();
            if (ObjectUtil.isNull(user_id)) {
                return this.setUnauthorizedResponse(exchange, ResultCode.UN_AUTHORIZED, "令牌验证失败");
            }
            //4、刷新登录token过期时间
            redisService.expire(RedisEnum.LOGIN_CACHE, token_key, CacheConstants.getTokenExpire(token_key));
        }
        //5、设置用户关键信息到请求头，方便微服务直接获取
        ServerHttpRequest mutableReq = exchange
                .getRequest()
                .mutate()
                .header(Constants.HEADER_TOKEN, token)
                .header(Constants.HEADER_USER_ID, ObjectUtil.isNotNull(user_id) ? user_id.toString() : "")
                .header(Constants.HEADER_PHONE_NUM, StrUtil.isNotEmpty(phone_num) ? phone_num : "")
                .header(Constants.HEADER_ENTERPRISE_ID, ObjectUtil.isNotNull(enterprise_id) ? enterprise_id.toString() : "")
                .header(Constants.HEADER_FUNCTION_FLAG, header.get(Constants.HEADER_FUNCTION_FLAG))
                .header(Constants.HEADER_APP_KEY, header.get(Constants.HEADER_APP_KEY))
                .header(Constants.HEADER_APP_VERSION, header.get(Constants.HEADER_APP_VERSION))
                .header(Constants.HEADER_CLIENT_TYPE, header.get(Constants.HEADER_CLIENT_TYPE))
                .header(Constants.HEADER_APP_CHANNEL, header.get(Constants.HEADER_APP_CHANNEL))
                .header(Constants.HEADER_DEVICE_ID, header.get(Constants.HEADER_DEVICE_ID))
                .header(Constants.HEADER_DEVICE_BRAND, header.get(Constants.HEADER_DEVICE_BRAND))
                .header(Constants.HEADER_DEVICE_MODEL, header.get(Constants.HEADER_DEVICE_MODEL))
                .header(Constants.HEADER_DEVICE_OS, header.get(Constants.HEADER_DEVICE_OS))
                .build();
        ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
        return chain.filter(mutableExchange);
    }

    private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, ResultCode resultCode, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);
        log.error("token过滤器异常:{},请求路径:{}", msg, exchange.getRequest().getPath());
        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(R.fail(resultCode, msg)));
        }));
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}